/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.service.security.kmc;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Random;
import java.util.TimeZone;
import java.util.Vector;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import org.apache.log4j.Logger;

import cgl.narada.discovery.broker.BrokerDiscoveryHelper;
import cgl.narada.discovery.broker.messages.BrokerDiscoveryResponse;
import cgl.narada.discovery.topics.Entity;
import cgl.narada.discovery.topics.messages.EncryptedMessagePayload;
import cgl.narada.discovery.topics.messages.SignedTopicAdvertisement;
import cgl.narada.event.NBEvent;
import cgl.narada.event.TemplateProfileAndSynopsisTypes;
import cgl.narada.matching.Profile;
import cgl.narada.service.ServiceException;
import cgl.narada.service.client.ClientService;
import cgl.narada.service.client.EventConsumer;
import cgl.narada.service.client.EventProducer;
import cgl.narada.service.client.NBEventListener;
import cgl.narada.service.client.SessionService;
import cgl.narada.service.security.kmc.messages.KMCMessage;
import cgl.narada.service.security.kmc.messages.SecureTopicKeyCreateUpdateRequest;
import cgl.narada.service.security.kmc.messages.SecureTopicKeyRequest;
import cgl.narada.service.security.kmc.messages.SecureTopicKeyResponse;
import cgl.narada.service.security.kmc.messages.SignedRequest;
import cgl.narada.service.security.securityprovider.CertificateManager;
import cgl.narada.service.security.securityprovider.CertificateUtil;
import cgl.narada.service.timer.ClockI;
import cgl.narada.service.timer.HRClock;
import cgl.narada.util.SystemInit;
import cgl.narada.util.SystemNotInitializedException;

/**
 * KMC Service is used to store and issue security tokens for secure topics.<br>
 * Currently the validity of the KMC service topic is set to
 * <code>Dec 31, 2005 23:59:59</code>. <br>
 * Note: You will need to change and recompile KMCService after this time. <br>
 * Created on Sep 8, 2005
 * 
 * @author Harshawardhan Gadgil (hgadgil@grids.ucs.indiana.edu)
 */
public class KMCService implements NBEventListener {

	static Logger log = Logger.getLogger("KMCService");

	private Entity _entity;

	private String KMC_ID = "kmc1";

	private int KMC_CLIENT_ID;

	private String KMC_TOPIC_UUID = "/services/KMC/" + KMC_ID;

	/** Stores a mapping of the topic -> MapEntry */
	private Hashtable topicSecretKeyMap;

	// My credentials
	private Certificate kmcCertificate;

	private PrivateKey kmcPrivateKey;

	private PublicKey rootPubKey;

	// Misc
	private EventProducer producer;

	private EventConsumer consumer;

	// Connection parameters
	private String brokerHost;

	private String brokerPort;

	private String brokerConnProtocol;

	// BECNHMARK
	private boolean benchmark = false;

	private ClockI hrClock = null;

	private FileOutputStream fout = null;

	// private String fileName = "/home/hgadgil/kmc.out";
	private String fileName = "c:/kmc.out";

	/**
	 * A structure encapsulating the details for a given topic synopsis <br>
	 * Created on Sep 9, 2005
	 * 
	 * @author Harshawardhan Gadgil (hgadgil@grids.ucs.indiana.edu)
	 */
	private class MapEntry {

		public SecretKey secureTopicKey;

		public Hashtable publishers;

		public Hashtable subscribers;

		public Calendar validUntil;
	};

	private int year = 2010;

	private int month = Calendar.DECEMBER;

	private int date = 31;

	private int hour = 23;

	private int min = 59;

	private int sec = 59;

	/**
	 * Creates a Key Management Center Service, initializes the service.
	 * Initialization entails creating a topic using Topic Discovery and
	 * subscribing to it. Assumes presence of at least 1 TDN connected to the
	 * broker network.
	 */
	public KMCService(int kmcID, String keystore, String pathToServiceConfig,
			String h, String p, String prot) {

		// Benchmark
		if (benchmark) {
			// Create the clock

			String nbHome = null;
			try {
				nbHome = SystemInit.getInitData(SystemInit.SYS_NB_HOME);
			} catch (SystemNotInitializedException e) {
				log.error("", e);
			}

			hrClock = HRClock.getClock(nbHome);
			try {
				fout = new FileOutputStream(fileName);
			} catch (FileNotFoundException e) {
				log.error("", e);
			}
		}

		brokerHost = h;
		brokerPort = p;
		brokerConnProtocol = prot;

		System.out.println("Connecting to " + brokerHost + ":" + brokerPort
				+ " [" + brokerConnProtocol + "]");

		// ---

		// Initialize mapping table
		topicSecretKeyMap = new Hashtable();

		// Generate a random Int to be the KMC's client ID
		KMC_CLIENT_ID = (new Random()).nextInt();

		// MUST INITIALIZE THE CertificateManager
		CertificateManager certMan = new CertificateManager();
		certMan.init(keystore, null);

		kmcCertificate = CertificateUtil.getCertificate(certMan, KMC_ID);
		kmcPrivateKey = CertificateUtil.getPrivateKey(certMan, KMC_ID);
		rootPubKey = CertificateUtil.getPublicKey(certMan,
				certMan.ROOT_CA_ALIAS);

		// Should use broker discovery when its in place...
		// For now just use some broker and port...

		_entity = new Entity(kmcID, pathToServiceConfig, KMC_ID,
				kmcCertificate, kmcPrivateKey, rootPubKey, brokerHost,
				brokerPort, brokerConnProtocol);

		// Generate KMC Secret topic
		if (_entity.sendTDNDiscoveryRequest(10000)) {
			log.info("Found TDN ! Proceeding to create TOPIC !!");

			Calendar until = Calendar
					.getInstance(TimeZone.getTimeZone("GMT-5"));
			until.set(year, month, date, hour, min, sec);

			Vector authorizedUsers = new Vector();
			try {
				String NB_HOME = SystemInit.getInitData(SystemInit.SYS_NB_HOME);
				BufferedReader br = new BufferedReader(new InputStreamReader(
						new FileInputStream(NB_HOME
								+ "/config/kmcAuthorizedUsers.txt")));
				String s;
				while ((s = br.readLine()) != null) {
					log.info("Adding authorized User: " + s);
					authorizedUsers.add(s);
				}
			} catch (Exception e) {
				log.error(e);
				e.printStackTrace();
			}

			log.info(authorizedUsers.size() + " authorized users !");
			boolean result = _entity.createTopic(KMC_ID, until,
					"KMC Secret Topic", TemplateProfileAndSynopsisTypes.STRING,
					KMC_TOPIC_UUID, authorizedUsers, 10000);
			if (result) {
				log.info("Topic Creation Successful !");
				log
						.debug("TOPIC UUID: "
								+ _entity.getTopicUUID(KMC_TOPIC_UUID));

				subscribeToTopic(_entity.getTopicUUID(KMC_TOPIC_UUID),
						KMC_CLIENT_ID, pathToServiceConfig);
			} else {
				log
						.error("TOPIC Creation Unsuccessful, Perhaps a timeout occurred OR TDN was unreachable !");
			}
		} else
			log.error("NO TDN found within specified timeout period !!");

		log.info("KMC Initialized...");
	}

	/**
	 * Subscribes to the specified topic UUID. this UUID represents the topic
	 * over which interested clients can contact this KMC
	 * 
	 * @param topicUUID -
	 *            String representing the string topic
	 */
	private void subscribeToTopic(String topicUUID, int clientEntityId,
			String configFilePath) {

		Properties prop = new Properties();

		// IMP: Might conflict w/ other ppl's entity IDs Maintain constant for
		// now
		int entityId = (new Random()).nextInt();

		prop.put("hostname", brokerHost);
		prop.put("portnum", brokerPort);

		log.info("Connecting using " + brokerConnProtocol + " @ " + brokerHost
				+ ":" + brokerPort + " EntityID[" + entityId + "]");

		try {
			ClientService clientService = SessionService.getClientService(
					clientEntityId, configFilePath);
			clientService.initializeBrokerCommunications(prop,
					brokerConnProtocol);
			consumer = clientService.createEventConsumer(this);
			producer = clientService.createEventProducer();
			producer.setSuppressRedistributionToSource(true);

			// Subscribe
			Profile profile = clientService.createProfile(
					TemplateProfileAndSynopsisTypes.STRING, topicUUID);
			consumer.subscribeTo(profile);

		} catch (ServiceException e) {
			log.error("", e);
		}
	}

	/**
	 * @param payload
	 */
	private void processSecureTopicKeyCreation(byte[] payload) {

		long start = 0, end = 0;

		// BENCHMARK
		if (benchmark) {
			start = hrClock.getTimeMicroseconds();
		}

		SignedRequest signedRequest = SignedRequest
				.createObjectFromBytes(payload);
		if (signedRequest == null) {
			log.warn("Could not retrieve Signed Request !");
			return;
		}

		if (!signedRequest.verify()) {
			log.error("Could not verify digital signature ! "
					+ "Message was probably tampered !");
			return;
		}

		// OK digital signature verified... now get the request and process it

		SecureTopicKeyCreateUpdateRequest request = SecureTopicKeyCreateUpdateRequest
				.createObjectFromBytes(signedRequest.getRequest());
		if (request == null) {
			log.warn("{KMC}: Garbage SECURE_TOPIC_KEY_CREATION recieved !");
			return;
		}

		// Check if this topic synopsis already exists in the table
		String topicSynopsis = request.getTopicAd().getTopicAd()
				.getTopicSynopsis();
		if (topicSecretKeyMap.containsKey(topicSynopsis)) {
			log.warn("Topic Key for specified topic synopsis already exists ! "
					+ "Overwriting...");

			// Similar process, but in this case we just overwrite
			// existing security key, IF all credentials are verified
			topicSecretKeyMap.remove(topicSynopsis);

			// TODO: Notify all brokers about the change in security key !
			// broadcastInvalidateSecurityKeyMessage();
		}

		// Step 2: Check if the credentials are valid
		SignedTopicAdvertisement sta = request.getTopicAd();
		X509Certificate cert = (X509Certificate) request.getOwnerCredentials();

		// Step 2a: Validate certificate
		if (!CertificateUtil.ValidateCertificate(cert, rootPubKey)) {
			log.error("Cannot validate entity Certificate! "
					+ "Discarding request...");
			notifyError("Cannot validate entity Certificate!", request
					.getResponseTopic());

			return;
		}

		// BENCHMARK
		if (benchmark) {
			end = hrClock.getTimeMicroseconds();
			try {
				fout.write(("Create&ValidateRequest: " + (end - start) + "\n")
						.getBytes());
				fout.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		// Step 2b: Validate payload signature
		if (sta.verify(cert.getPublicKey())) {

			// BENCHMARK
			if (benchmark) {
				start = hrClock.getTimeMicroseconds();
			}

			// Ok now create the security key and store the mappings...

			// Issue a topic key to the owner
			Certificate clientCert = request.getOwnerCredentials();

			MapEntry entry = new MapEntry();
			entry.publishers = request.getAllowedPublishers();
			entry.subscribers = request.getAllowedSubscribers();

			String subjectDN = ((X509Certificate) clientCert).getSubjectDN()
					.getName();

			// Generate topic key

			// KeyGenerator kg = KeyGenerator.getInstance("DES");

			try {
				log.info("GENERATING: " + request.getKeySize() + " bit, "
						+ request.getAlgorithm() + " Secret Key");
				KeyGenerator kg = KeyGenerator.getInstance(request
						.getAlgorithm());
				kg.init(request.getKeySize(), new SecureRandom());
				entry.secureTopicKey = kg.generateKey();
			} catch (NoSuchAlgorithmException e) {
				log.error("Unable to generate Topic Key", e);
				return;
			}

			// BENCHMARK
			if (benchmark) {
				end = hrClock.getTimeMicroseconds();
				try {
					fout.write(("Generate Secret KEY: " + (end - start) + "\n")
							.getBytes());
					fout.flush();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			// BENCHMARK
			if (benchmark) {
				start = hrClock.getTimeMicroseconds();
			}

			// Set the validity of the token
			Calendar validUntil = request.getValidity();

			entry.validUntil = getValidity(validUntil, sta.getTopicAd()
					.getLifetime());

			// Set the entity's topic rights
			int rights = 0;
			if (checkAuthorization(entry.publishers, subjectDN,
					entry.validUntil))
				rights |= TopicRights.PUBLISH_RIGHT;

			if (checkAuthorization(entry.subscribers, subjectDN,
					entry.validUntil))
				rights |= TopicRights.SUBSCRIBE_RIGHT;

			TopicRights topicRights = new TopicRights(rights);

			// Generate the security token for the topic owner and sign
			// it using my private key
			SecurityToken token = new SecurityToken(clientCert, topicRights,
					validUntil);
			SignedSecurityToken sst = new SignedSecurityToken(token,
					kmcCertificate);
			if (!sst.generateSignedDigest(kmcPrivateKey)) {
				log.error("Error Signing Security Token ! Aborting");
				return;
			}

			// Store token and other stuff in appropriate tables
			topicSecretKeyMap.put(topicSynopsis, entry);

			// Finally, SEND the encrypted topic key and security token
			// to the requestor
			SecureTopicKeyResponse response = new SecureTopicKeyResponse(
					entry.secureTopicKey, sst);

			EncryptedMessagePayload empResp = EncryptedMessagePayload.generate(
					response.getBytes(), clientCert.getPublicKey());

			// BENCHMARK
			if (benchmark) {
				end = hrClock.getTimeMicroseconds();
				try {
					fout
							.write(("GenerateSecurityToken: " + (end - start) + "\n")
									.getBytes());
					fout.flush();
				} catch (IOException e) {
					e.printStackTrace();
					System.exit(-1);
				}
			}

			byte[] empRespBytes = empResp.getBytes();

			byte[] msg = new byte[empRespBytes.length + 1];
			msg[0] = KMCClient.MSG_SECURITY_TOKEN;
			System.arraycopy(empRespBytes, 0, msg, 1, empRespBytes.length);

			send(request.getResponseTopic(), msg);
		} else {
			// Looks like somebody is faking the certs...Just drop the
			// request
			log.warn("Could not verify Topic Ad's digital signature !");
		}

		// BENCHMARKing purposes only !
		// topicSecretKeyMap.clear();
	}

	private void processSecureTopicKeyRequest(byte[] payload) {
		long start = 0, end = 0;

		// BENCHMARK
		if (benchmark) {
			start = hrClock.getTimeMicroseconds();
		}

		SecureTopicKeyRequest topicKeyRequest = SecureTopicKeyRequest
				.createObjectFromBytes(payload);
		if (topicKeyRequest == null) {
			log.warn("{KMC}: Garbage SECURE_TOPIC_KEY_CREATION recieved !");
			return;
		}

		// Step 2: Check if the credentials are valid
		// topicKeyRequest

		Certificate cert = topicKeyRequest.getCredentials();
		if (!CertificateUtil.ValidateCertificate(cert, rootPubKey)) {
			log.error("Unable to verify credentials ! Discarding request");
			notifyError("Unable to verify credentials !", topicKeyRequest
					.getResponseTopic());
			return;
		}

		// Step 3: Check if the KMC has a topic key entry for the
		// requested topic synopsis
		if (!topicSecretKeyMap.containsKey(topicKeyRequest.getTopicSynopsis())) {
			log.error("No security Token found for specified Synopsis !");
			notifyError("No security Token found for specified Synopsis !",
					topicKeyRequest.getResponseTopic());
			return;
		}

		// BENCHMARK
		if (benchmark) {
			end = hrClock.getTimeMicroseconds();
			try {
				fout.write(("Create&ValidateRequest: " + (end - start) + "\n")
						.getBytes());
				fout.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		// Step 4: Check if the entity has the rights it has requested
		// NOTE: Only if the requested rights match the available rights
		// then a response is sent
		// For e.g.: If the entity requests publish and subscribe rights
		// but
		// has onlypublish rights, no response is sent

		// BENCHMARK
		if (benchmark) {
			start = hrClock.getTimeMicroseconds();
		}

		MapEntry entry = (MapEntry) topicSecretKeyMap.get(topicKeyRequest
				.getTopicSynopsis());

		TopicRights reqRights = topicKeyRequest.getRequestedRights();
		String entityDN = ((X509Certificate) cert).getSubjectDN().getName();
		boolean valid = true;

		// MUST Request a token for every access ?
		Calendar validUntil = null;
		if (reqRights.hasPublishRight()) {
			if (!checkAuthorization(entry.publishers, entityDN, null))
				valid &= false;
			else {
				log.info("PUBLISH_RIGHT Authorized !");
				validUntil = (Calendar) entry.publishers.get(entityDN);
			}
		} else if (reqRights.hasSubscribeRight()) {
			if (!checkAuthorization(entry.subscribers, entityDN, null))
				valid &= false;
			else {
				log.info("SUBSCRIBE_RIGHT Authorized !");
				validUntil = (Calendar) entry.subscribers.get(entityDN);
			}
		}

		if (!valid) {
			log.error("Specified entity does not have correct rights ! "
					+ "Discarding request..");
			notifyError("Entity does not have requested rights !",
					topicKeyRequest.getResponseTopic());
			return;
		}

		// BENCHMARK
		if (benchmark) {
			end = hrClock.getTimeMicroseconds();
			try {
				fout.write(("CheckRights: " + (end - start) + "\n").getBytes());
				fout.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		// BENCHMARK
		if (benchmark) {
			start = hrClock.getTimeMicroseconds();
		}

		// Step 5: Ok, Everythings cool, so send the security token and
		// the secure topic key

		// Generate the security token for the topic owner and sign
		// it using my private key
		SecurityToken token = new SecurityToken(cert, reqRights, validUntil);
		SignedSecurityToken sst = new SignedSecurityToken(token, kmcCertificate);

		if (!sst.generateSignedDigest(kmcPrivateKey)) {
			log.error("Error Signing Security Token ! Aborting");
			notifyError("Error Signing Security Token !", topicKeyRequest
					.getResponseTopic());
			return;
		}

		// Finally, SEND the encrypted topic key and security token
		// to the requestor
		SecureTopicKeyResponse response = new SecureTopicKeyResponse(
				entry.secureTopicKey, sst);
		EncryptedMessagePayload empResp = EncryptedMessagePayload.generate(
				response.getBytes(), cert.getPublicKey());

		// BENCHMARK
		if (benchmark) {
			end = hrClock.getTimeMicroseconds();
			try {
				fout.write(("GenerateSecurityToken: " + (end - start) + "\n")
						.getBytes());
				fout.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		byte[] empRespBytes = empResp.getBytes();

		byte[] msg = new byte[empRespBytes.length + 1];
		msg[0] = KMCClient.MSG_SECURITY_TOKEN;
		System.arraycopy(empRespBytes, 0, msg, 1, empRespBytes.length);

		send(topicKeyRequest.getResponseTopic(), msg);

	}

	/**
	 * Process Requests sent to the KMC Service
	 */
	public void onEvent(NBEvent nbEvent) {
		long start = 0, end = 0;

		// We can recieve 3 types of requests
		// 1. First time request to publish / subscribe. We create the secret
		// key and store the mapping in the table
		// 2. Additional requests to publish / subscribe. In this case we lookup
		// and see if the requestor has rights. Only respond if rights match.
		// 3. An encrypted number. If this number matches the one sent by KMC
		// previously then this validates the owner.

		KMCMessage kmcMessage = KMCMessage.createObjectFromBytes(nbEvent
				.getContentPayload());

		if (kmcMessage != null) {

			EncryptedMessagePayload emp = EncryptedMessagePayload
					.createObjectFromBytes(kmcMessage.getPayload());
			if (emp == null) {
				log.warn("{KMC}: Garbage Message recieved ! "
						+ "Unable to get Encrypted message payload");
				return;
			}

			byte[] payload = emp.getDecryptedPayload(kmcPrivateKey);

			// Step 0: Check the type of message
			switch (kmcMessage.getMessageType()) {

			case KMCMessage.KmcMSG_SECURE_TOPIC_KEY_CREATE_MODIFY:

				// BENCHMARK
				if (benchmark) {
					start = hrClock.getTimeMicroseconds();
				}

				processSecureTopicKeyCreation(payload);

				// BENCHMARK
				if (benchmark) {
					end = hrClock.getTimeMicroseconds();
					try {
						fout.write(("processSecureTopicKeyCreation: "
								+ (end - start) + "\n").getBytes());
						fout.flush();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				break;

			case KMCMessage.KmcMSG_SECURE_TOPIC_KEY_REQUEST:
				// BENCHMARK
				if (benchmark) {
					start = hrClock.getTimeMicroseconds();
				}

				processSecureTopicKeyRequest(payload);

				// BENCHMARK
				if (benchmark) {
					end = hrClock.getTimeMicroseconds();
					try {
						fout.write(("processSecureTopicKeyRequest: "
								+ (end - start) + "\n").getBytes());
						fout.flush();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				break;

			default:
				log.warn("{KMC}: Garbage EVENT recieved ! "
						+ "Unknown KMCMessage Type...");
				break;
			}

		} else {
			// OOPS, Garbage message...
			log.warn("{KMC}: Garbage EVENT recieved ! Unable to decrypt..");
			return;
		}

		// Testing purpose only !!
		// this.topicSecretKeyMap.clear();
	}

	private void notifyError(String msg, String dest) {
		byte[] msgBytes = msg.getBytes();
		byte[] payload = new byte[msgBytes.length + 1];

		payload[0] = KMCClient.MSG_ERROR;
		System.arraycopy(msgBytes, 0, payload, 1, msgBytes.length);

		send(dest, payload);
	}

	/**
	 * Checks if the entity specified by the DN is contained in the list<br>
	 * Also adjusts the maximum validity of the right TODO: Add regexp matching
	 * of DN (this'll check for partial matches...)
	 * 
	 * @param list
	 * @param dn
	 * @param maxValidity -
	 *            The maximum validity the entity 'dn' can have. This check is
	 *            not performed if maxValidity is null
	 * @return TRUE if the entity is in the list, FALSE otherwise
	 */
	private boolean checkAuthorization(Hashtable list, String dn,
			Calendar maxValidity) {
		log.info("Checking rights for: " + dn);

		if (list.containsKey(dn)) {
			if (maxValidity != null) {
				Calendar requestedValidity = (Calendar) list.get(dn);
				list.put(dn, getValidity(requestedValidity, maxValidity));
			}
			return true;
		}
		return false;

	}

	/**
	 * If the requested validity is <= max validity then we return the
	 * requestedValidy else we return the max validity
	 * 
	 * @param requestedValidity
	 * @param maxValidity
	 * @return
	 */
	private Calendar getValidity(Calendar requestedValidity,
			Calendar maxValidity) {
		if (requestedValidity.before(maxValidity)
				|| requestedValidity.equals(maxValidity))
			return requestedValidity;
		else
			return maxValidity;
	}

	/**
	 * Send the specified message on specified topic
	 * 
	 * @param topicName -
	 *            The topic on which to send the message
	 * @param request -
	 *            The message to be sent
	 */
	public void send(String topicName, byte[] request) {
		producer.generateEventIdentifier(true);
		producer.setTemplateId(12345);
		producer.setDisableTimestamp(false);
		try {
			NBEvent nbEvent = producer.generateEvent(
					TemplateProfileAndSynopsisTypes.STRING, topicName, request);
			producer.publishEvent(nbEvent);
			log
					.info("sent " + request.length + " bytes ON <" + topicName
							+ ">");
		} catch (ServiceException e) {
			log.error("", e);
		}
	}

	/**
	 * Starts the Key Management Center Service. Use -help for usage<br>
	 * TODO: Add parsing of command line parameters to provide more fine grained
	 * control
	 */

	public static void main(String[] args) {
		if (args[0].equals("-help")) {
			System.out.println("USAGE: ");
			System.out
					.println("ARG_0: <hostnameToUse> OR -auto (for using broker discovery");
			System.out.println("ARG_1: <port>");
			System.out.println("ARG_2: <protocol>");
			System.out.println("-----------------------");
			System.exit(0);
		}

		SystemInit.init();

		String host = "localhost"; // ba[0].brokerInfo.getHostname();
		String port = "25000"; // props.getProperty("NIOTCPServerPort");
		String protocol = "niotcp";

		String serviceConfigPath = null;
		String keystore = null;
		try {
			serviceConfigPath = SystemInit
					.getInitData(SystemInit.SYS_SERVICE_CONFIG_PATH);
			keystore = SystemInit.getInitData(SystemInit.SYS_KEYSTORE);
		} catch (SystemNotInitializedException e) {
			e.printStackTrace();
		}

		if (args.length > 0) {
			host = args[0];
			if (host.equals("-auto")) {
				// invoke Broker Discovery
				// USE Broker Discovery to locate broker !
				// BROKER DISCOVERY:---

				BrokerDiscoveryHelper bdh = new BrokerDiscoveryHelper(
						(new Random()).nextInt(), serviceConfigPath, 5000, 1, 1);

				BrokerDiscoveryResponse[] ba = bdh.discover(
						new String[] { protocol }, "**", "*");

				Properties props = (Properties) ba[0].brokerInfo
						.getSupportedTransportProtocols().get("niotcp");

				host = ba[0].brokerInfo.getHostname();
				port = props.getProperty("NIOTCPServerPort");
			}
			// ---
			else {
				if (args.length > 1)
					port = args[1];
				if (args.length > 2)
					protocol = args[2];
			}
		}

		KMCService kmcService = new KMCService(99, keystore, serviceConfigPath,
				host, port, protocol);
	}
}
